針對滑鼠、鍵盤平移或水平縮放圖表,像是選取區間或是資料太密集需要縮放觀看時使用。
建立zoom
縮放事件。此API會替我們綁上監聽各種縮放事件。
// 當有縮放事件時,會發出zoom此事件。
selection.call(d3.zoom().on("zoom", zoomed));
// 可單純針對滾論滾動進行監聽。
selection
.call(zoom)
.on("wheel.zoom", null);
zoom
針對各種事件細節列表。
Event | Listening Element | Zoom Event | Default Prevented? |
---|---|---|---|
mousedown⁵ | selection | start | no¹ |
mousemove² | window¹ | zoom | yes |
mouseup² | window¹ | end | yes |
dragstart² | window | - | yes |
selectstart² | window | - | yes |
click³ | window | - | yes |
dblclick | selection | multiple⁶ | yes |
wheel⁸ | selection | zoom⁷ | yes |
touchstart | selection | multiple⁶ | no⁴ |
touchmove | selection | zoom | yes |
touchend | selection | end | no⁴ |
touchcancel | selection | end | no⁴ |
先來製作基本圖表
const data = [
{name: "E", value: 0.12702},
{name: "T", value: 0.09056},
{name: "A", value: 0.08167},
{name: "O", value: 0.07507},
{name: "I", value: 0.06966},
{name: "N", value: 0.06749},
{name: "S", value: 0.06327},
{name: "H", value: 0.06094},
{name: "R", value: 0.05987},
{name: "D", value: 0.04253},
{name: "L", value: 0.04025},
{name: "C", value: 0.02782},
{name: "U", value: 0.02758},
{name: "M", value: 0.02406},
{name: "W", value: 0.0236},
{name: "F", value: 0.02288},
{name: "G", value: 0.02015},
{name: "Y", value: 0.01974},
{name: "P", value: 0.01929},
{name: "B", value: 0.01492},
{name: "V", value: 0.00978},
{name: "K", value: 0.00772},
{name: "J", value: 0.00153},
{name: "X", value: 0.0015},
{name: "Q", value: 0.00095},
{name: "Z", value: 0.00074},
// ["letter", "frequency"]
]
const width = 800;
const height = 800;
const padding = {
top: 20,
right: 0,
bottom: 30,
left: 40
};
const svg = d3.select('svg').attr('width', width).attr('height', height);
const xScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([padding.left, width - padding.right])
.padding(0.1)
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([height - padding.bottom, padding.top])
const xAxis = g => g
.attr("transform", `translate(0,${height - padding.bottom})`)
.call(d3.axisBottom(xScale).tickSizeOuter(0))
const yAxis = g => g
.attr("transform", `translate(${padding.left},0)`)
.call(d3.axisLeft(yScale))
.call(g => g.select(".domain").remove())
svg.append("g")
.attr("class", "bars")
.attr("fill", "steelblue")
.selectAll("rect")
.data(data)
.join("rect")
.each(data => console.log(data))
.attr("x", d => xScale(d.name))
.attr("y", d => yScale(d.value))
.attr("height", d => yScale(0) - yScale(d.value))
.attr("width", xScale.bandwidth());
svg.append("g")
.attr("class", "x-axis")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis")
.call(yAxis);
function zoom(svg) {
const extent = [[padding.left, padding.top], [width - padding.right, height - padding.top]];
// 確定目前寬度最大最小範圍
// 傳進來的svg綁上zoom事件
svg.call(d3.zoom()
.scaleExtent([1, 8])
// zoom scaleExtent設置最大最小縮放比例
.translateExtent(extent)
// 可縮放的最大最小範圍
.extent(extent)
// 設定最大最小範圍
.on("zoom", zoomed));
// 當縮放事件發生時,對應的事件
function zoomed(event) {
xScale.range([padding.left, width - padding.right].map(d => event.transform.applyX(d)));
// 重新計算xScale
svg.selectAll(".bars rect").attr("x", d => xScale(d.name)).attr("width", xScale.bandwidth());
// 並將所有的rect重新計算寬度以及位置
svg.selectAll(".x-axis").call(xAxis);
// 重新產生X軸比例尺
}
}
透過svg
的call
來執行zoom
的方法。
const data = [
{name: "E", value: 0.12702},
{name: "T", value: 0.09056},
{name: "A", value: 0.08167},
{name: "O", value: 0.07507},
{name: "I", value: 0.06966},
{name: "N", value: 0.06749},
{name: "S", value: 0.06327},
{name: "H", value: 0.06094},
{name: "R", value: 0.05987},
{name: "D", value: 0.04253},
{name: "L", value: 0.04025},
{name: "C", value: 0.02782},
{name: "U", value: 0.02758},
{name: "M", value: 0.02406},
{name: "W", value: 0.0236},
{name: "F", value: 0.02288},
{name: "G", value: 0.02015},
{name: "Y", value: 0.01974},
{name: "P", value: 0.01929},
{name: "B", value: 0.01492},
{name: "V", value: 0.00978},
{name: "K", value: 0.00772},
{name: "J", value: 0.00153},
{name: "X", value: 0.0015},
{name: "Q", value: 0.00095},
{name: "Z", value: 0.00074},
// ["letter", "frequency"]
]
const width = 800;
const height = 800;
const padding = {
top: 20,
right: 0,
bottom: 30,
left: 40
};
// 關鍵在最後將執行zoom事件,會帶入此svg selection並執行zoom method。
const svg = d3.select('svg').attr('width', width).attr('height', height).call(zoom)
const xScale = d3.scaleBand()
.domain(data.map(d => d.name))
.range([padding.left, width - padding.right])
.padding(0.1)
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)]).nice()
.range([height - padding.bottom, padding.top])
const xAxis = g => g
.attr("transform", `translate(0,${height - padding.bottom})`)
.call(d3.axisBottom(xScale).tickSizeOuter(0))
const yAxis = g => g
.attr("transform", `translate(${padding.left},0)`)
.call(d3.axisLeft(yScale))
.call(g => g.select(".domain").remove())
svg.append("g")
.attr("class", "bars")
.attr("fill", "steelblue")
.selectAll("rect")
.data(data)
.join("rect")
.each(data => console.log(data))
.attr("x", d => xScale(d.name))
.attr("y", d => yScale(d.value))
.attr("height", d => yScale(0) - yScale(d.value))
.attr("width", xScale.bandwidth());
svg.append("g")
.attr("class", "x-axis")
.call(xAxis);
svg.append("g")
.attr("class", "y-axis")
.call(yAxis);
function zoom(svg) {
const extent = [[padding.left, padding.top], [width - padding.right, height - padding.top]];
svg.call(d3.zoom()
.scaleExtent([1, 8])
.translateExtent(extent)
.extent(extent)
.on("zoom", zoomed));
function zoomed(event) {
xScale.range([padding.left, width - padding.right].map(d => event.transform.applyX(d)));
svg.selectAll(".bars rect").attr("x", d => xScale(d.name)).attr("width", xScale.bandwidth());
svg.selectAll(".x-axis").call(xAxis);
}
}
zoom
還有其他選取範圍或是垂直水平縮放,此範例僅使用水平縮放。
範例連結
Codepen